package io.github.novaframe.web;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.github.novaframe.CodeException;
import io.github.novaframe.config.NovaframeCode;
import io.github.novaframe.web.error.CustomParameterizedException;
import io.github.novaframe.web.error.ErrorVM;
import io.github.novaframe.web.error.ParameterizedErrorVM;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.List;

/**
 * 全局异常处理类
 * JSON
 * 
 * @author Locki
 * @date Feb 27, 2017 3:22:18 PM
 * @version 1.0
 *
 */
@ControllerAdvice
public class ExceptionJsonTranslator {
	
	private final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 数据并发异常
     * @return
     */
	@ExceptionHandler(ConcurrencyFailureException.class)
	@ResponseStatus(HttpStatus.CONFLICT)
	@ResponseBody
	public ErrorVM processConcurrencyError(ConcurrencyFailureException ex) {
	    logger.error("processConcurrencyError error=> ", ex);
		return new ErrorVM(NovaframeCode.CONCURRENCY_FAILURE);
	}

    /**
     * web参数绑定校验失败
      */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public ErrorVM processValidationError(MethodArgumentNotValidException ex) {
        logger.error("processValidationError error=> ", ex);
		BindingResult result = ex.getBindingResult();
		List<FieldError> fieldErrors = result.getFieldErrors();
        ErrorVM dto = new ErrorVM(NovaframeCode.VALIDATION_FAILURE);
        for (FieldError fieldError : fieldErrors) {
            dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode());
        }
        return dto;
	}

    /**
     * MethodArgumentTypeMismatchException
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorVM processMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        logger.error("processServletRequestBindingException error=> ", ex);
        return new ErrorVM(NovaframeCode.VALIDATION_FAILURE.value(), ex.getMessage());
    }

    /**
     * Request参数绑定异常
     * @param ex
     * @return
     */
    @ExceptionHandler(ServletRequestBindingException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorVM processServletRequestBindingException(ServletRequestBindingException ex) {
        logger.error("processServletRequestBindingException error=> ", ex);
        return new ErrorVM(NovaframeCode.VALIDATION_FAILURE.value(), ex.getMessage());
    }

    /**
     * 禁止访问
     * @param ex
     * @return
     */
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ResponseBody
    public ErrorVM processAccessDeniedException(AccessDeniedException ex) {
        logger.error("processAccessDeniedException error=> ", ex);
        return new ErrorVM(NovaframeCode.ACCESS_DENIED.value(), ex.getMessage());
    }
	
    /**
     * HTTP方法不支持
     * @param ex
     * @return
     */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        logger.error("processMethodNotSupportedException error=> ", ex);
        return new ErrorVM(NovaframeCode.METHOD_NOT_SUPPORTED.value(), ex.getMessage());
    }

    /**
     * 错误的参数
     * @param ex
     * @return
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorVM processIllegalArgumentException(IllegalArgumentException ex) {
        logger.error("processIllegalArgumentException error=> ", ex);
        return new ErrorVM(NovaframeCode.INVALID_PARAMTER.value(), ex.getMessage());
    }
	
    /**
     * 带业务状态码的异常
     * @param ex
     * @return
     */
	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(value = CodeException.class)
	public ErrorVM codeExceptionHandler(CodeException ex) {
		logger.error("codeExceptionHandler error=>", ex);
        return new ErrorVM(ex.getErrcode(), ex.getErrmsg());
	}

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = JsonProcessingException.class)
    public ErrorVM jsonParseExceptionHandler(JsonProcessingException ex) {
        logger.error("jsonParseExceptionHandler error=> ", ex);
        return new ErrorVM(NovaframeCode.INVALID_PARAMTER.value(), ex.getMessage());
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public ErrorVM processHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        logger.error("processHttpMessageNotReadableException error=> ", ex);
        return new ErrorVM(NovaframeCode.INVALID_PARAMTER.value(), ex.getMessage());
    }

    /**
     * 自定义带参数的异常
     * @param ex
     * @return
     */
    @ExceptionHandler(CustomParameterizedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) {
        logger.error("processParameterizedValidationError error=> ", ex);
        return ex.getErrorVM();
    }
	
    /**
     * 其他运行时异常
     * HttpMediaTypeNotSupportedException: Http参数类型错误
     * @param ex
     * @return
     */
	@ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResponseEntity<ErrorVM> processRuntimeException(Exception ex) {
        logger.error("processRuntimeException error=> ", ex);
        BodyBuilder builder;
        ErrorVM errorVM;
        // 获取返回状态 注解
        ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
        if (responseStatus != null) {
            builder = ResponseEntity.status(responseStatus.value());
            errorVM = new ErrorVM(responseStatus.value().value(), responseStatus.reason());
        } else {
            builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
            errorVM = new ErrorVM(NovaframeCode.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
        }
        return builder.body(errorVM);
    }
}
